<template>
    <context-holder />
    <viewUsers v-if="callType === CALL_TYPE.INIT" :users="allUsers" @call="sendMessageCall"></viewUsers>
    <div v-if="callType === CALL_TYPE.TEXT" class="chat-wrapper">
        <div class="chat-title">
            <span>{{ chatModel.otherName }}</span>
            <a-button type="text" @click="closeChat(1)">结束对话</a-button>
        </div>
        <div class="chat-content">
            <p class="chat-message" v-for="(item, index) in chatModel.messages" :key="index">
                <span :class="item.left ? 'left' : 'right'">{{ item.content }}</span>
            </p>
        </div>
        <div class="message-wrapper">
            <a-input v-model:value="msg" placeholder="输入消息" @keyup.enter="sendMessage"></a-input>
            <a-button class="btn-send" @click="sendMessage">发送</a-button>
            <a-button class="btn-audio" type="primary" ghost @click="sendAudioCall">语音通话</a-button>
            <a-button class="btn-video" type="primary" ghost @click="sendVideoCall">视频通话</a-button>
        </div>
    </div>
    <div v-if="callType === CALL_TYPE.AUDIO" class="audio-content">
        <video ref="audioRef" autoplay playsinline style="opacity: 0"></video>
        <a-avatar style="background-color: #6b798e" :size="80">{{ chatModel.otherName[0] }}</a-avatar>
        <span class="otherName">{{ chatModel.otherName }}</span>
        <a-button type="primary" danger :icon="h(AudioMutedOutlined)" @click="closeAudio"></a-button>
    </div>
    <div v-if="callType === CALL_TYPE.VIDEO" class="video-content">
        <video ref="videoCallerRef" class="video1" autoplay playsinline muted></video>
        <video ref="videoRecieverRef" class="video2" autoplay playsinline controls></video>
        <video ref="videoRecieverAudioRef" class="video3" autoplay style="width: 0; height: 0; opacity: 0"></video>
        <a-button type="primary" danger :icon="h(AudioMutedOutlined)" @click="closeVideo"></a-button>
    </div>
    <a-modal :title="waitCallModel.title" v-model:open="waitCallModel.visible" :maskClosable="false" :closable="false">
        <template #footer>
            <a-button type="primary" @click="closeChat(callType === CALL_TYPE.TEXT ? 3 : 2)">取消</a-button>
        </template>
    </a-modal>
    <a-modal
        :title="confirmCallModel.title"
        v-model:open="confirmCallModel.visible"
        cancelText="拒绝"
        okText="接受"
        :maskClosable="false"
        :closable="false"
        @cancel="onReject"
        @ok="onAgree"></a-modal>
</template>

<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, nextTick, h } from "vue";
import { AudioMutedOutlined } from "@ant-design/icons-vue";
import IMSocket from "../../utils/socket";
import viewUsers from "./viewUsers.vue";
import { message } from "ant-design-vue";
import { useSocketStore } from "../../stores/imsocket";
import peer from "../../utils/peer";

const [messageApi, contextHolder] = message.useMessage();

const CALL_TYPE = {
    INIT: -1,
    TEXT: 0,
    AUDIO: 1,
    VIDEO: 2
};
onMounted(() => {
    // 连接服务器
    initSocket();
});
onBeforeUnmount(() => {
    imSocket.socket.disconnect();
});

let imSocket: any = null;
const callType = ref(CALL_TYPE.INIT); // -1-初始化 0-聊天中 1-视频中 2-语音中
const msg = ref<string>("");
const socketStore = useSocketStore();
const allUsers = ref<any>([]);

const initSocket = () => {
    imSocket = IMSocket.getInstance();
    imSocket.bindEvents({
        onError: onerror,
        onConnect: onSocketConnect,
        onChatCall: onChatCall,
        onChatRes: onChatRes,
        onChatClose: onChatClose,
        onMessage: onMessage,
        onSdpCall: onSdpCall,
        onSdpRes: onSdpRes,
        onSdpClose: onSdpClose,
        onUserConnect: onUserConnect,
        onSdp: onSdp
    });
};

const onerror = () => {
    messageApi.error("连接服务器失败，请检查网络情况");
    socketStore.state = "服务器连接失败";
};

// 连接socket，注册用户
const onSocketConnect = () => {
    socketStore.state = "服务器初始化成功";
    messageApi.info("已连接到服务器");
    imSocket.socket.emit("register", {
        userName: socketStore.userName,
        userId: socketStore.userId
    });
};

// 发起聊天
const waitCallModel = ref({
    title: "等待应答",
    visible: false,
    callId: ""
});
const confirmCallModel = ref({
    title: "",
    callId: "",
    callType: CALL_TYPE.TEXT,
    visible: false
});
const sendMessageCall = (data: { id: string; name: string }) => {
    imSocket.socket.emit("chat_call", {
        userId: socketStore.userId,
        callId: data.id
    });
    waitCallModel.value.title = `等待${data.name}应答...`;
    waitCallModel.value.callId = data.id;
    waitCallModel.value.visible = true;
};

const onChatCall = (data: any) => {
    confirmCallModel.value.title = `用户: ${data.callName}向你发送聊天请求`;
    confirmCallModel.value.callId = data.callId;
    confirmCallModel.value.visible = true;
    confirmCallModel.value.callType = CALL_TYPE.TEXT;
};

const onChatRes = (data: any) => {
    if (data.isAgree) {
        waitCallModel.value.visible = false;
        // 同意，下一步
        callType.value = CALL_TYPE.TEXT;
        chatModel.value.otherName = allUsers.value.find(
            (item: any) => item.userId === waitCallModel.value.callId
        ).userName;
        chatModel.value.otherId = waitCallModel.value.callId;
    } else {
        waitCallModel.value.visible = false;
        messageApi.error("对方拒绝了你的聊天");
    }
};

// type = 1 退出聊天 type = 2 取消呼叫 type = 3 取消呼叫语音/视频
const closeChat = (type: number) => {
    if (type === 3) {
        imSocket.socket.emit("sdp_close", {
            callId: chatModel.value.otherId
        });
        messageApi.info("已取消");
        waitCallModel.value.visible = false;
        callType.value = CALL_TYPE.TEXT;
    } else {
        imSocket.socket.emit("chat_close", {
            userId: socketStore.userId,
            callId: type === 1 ? chatModel.value.otherId : waitCallModel.value.callId
        });
        callType.value = CALL_TYPE.INIT;
        chatModel.value.messages = [];
        waitCallModel.value.visible = false;
    }
};

const onChatClose = () => {
    messageApi.info(`对方取消了聊天`);
    callType.value = CALL_TYPE.INIT;
    chatModel.value.messages = [];
    waitCallModel.value.visible = false;
    confirmCallModel.value.visible = false;
    closeMedia();
};

// 拒绝
const onReject = () => {
    switch (confirmCallModel.value.callType) {
        case CALL_TYPE.TEXT: {
            imSocket.socket.emit("chat_res", {
                userId: socketStore.userId,
                callId: confirmCallModel.value.callId,
                isAgree: false
            });
            break;
        }
        case CALL_TYPE.AUDIO:
        case CALL_TYPE.VIDEO: {
            imSocket.socket.emit("sdp_res", {
                userId: socketStore.userId,
                callId: confirmCallModel.value.callId,
                isAgree: false,
                callType: confirmCallModel.value.callType
            });
            break;
        }
    }
    confirmCallModel.value.visible = false;
    messageApi.success("已拒绝");
};
// 接受
const onAgree = () => {
    confirmCallModel.value.visible = false;
    switch (confirmCallModel.value.callType) {
        case CALL_TYPE.TEXT: {
            imSocket.socket.emit("chat_res", {
                userId: socketStore.userId,
                callId: confirmCallModel.value.callId,
                isAgree: true
            });
            // 下一步。跳转聊天
            chatModel.value.otherName = allUsers.value.find(
                (item: any) => item.userId === confirmCallModel.value.callId
            ).userName;
            chatModel.value.otherId = confirmCallModel.value.callId;
            callType.value = CALL_TYPE.TEXT;
            break;
        }
        case CALL_TYPE.AUDIO:
        case CALL_TYPE.VIDEO: {
            callType.value = confirmCallModel.value.callType;
            imSocket.socket.emit("sdp_res", {
                userId: socketStore.userId,
                callId: confirmCallModel.value.callId,
                isAgree: true,
                callType: confirmCallModel.value.callType
            });
            break;
        }
    }
};

const chatModel = ref<any>({
    messages: [],
    otherName: "",
    otherId: ""
});
// 收到对方消息
const onMessage = (data: any) => {
    chatModel.value.messages.push({
        left: true,
        content: data
    });
    scrollBottom();
};

const sendMessage = () => {
    if (msg.value === "") {
        messageApi.warning("请不要发空消息");
        return;
    }
    let data = {
        callId: chatModel.value.otherId,
        content: msg.value
    };
    console.log(data);

    imSocket.socket.emit("message", data);

    chatModel.value.messages.push({
        left: false,
        content: msg.value
    });
    msg.value = "";
    scrollBottom();
};

const onUserConnect = (data: any) => {
    allUsers.value = data;
};

const scrollBottom = () => {
    nextTick(() => {
        const chat = document.querySelector(".chat-content") as HTMLDivElement;
        chat.scrollTo({ behavior: "smooth", top: chat.scrollHeight });
    });
};

/**
 * 音视频聊天模块
 */
const sdpModel = ref({
    audioVisible: false,
    videoVisible: false
});

/** 语音聊天模块 */
const sendAudioCall = () => {
    imSocket.socket.emit("sdp_call", {
        userId: socketStore.userId,
        callId: chatModel.value.otherId,
        callType: CALL_TYPE.AUDIO
    });
    waitCallModel.value.title = `等待${chatModel.value.otherName}应答...`;
    waitCallModel.value.visible = true;
};
const startAudio = async () => {
    await createPeer();
    await openMedia();
    sendCall();
};
const closeAudio = () => {
    messageApi.info("通话已结束");
    sdpModel.value.audioVisible = false;
    sdpModel.value.videoVisible = false;
    callType.value = CALL_TYPE.TEXT;
    imSocket.socket.emit("sdp_close", {
        callId: chatModel.value.otherId
    });
    closeMedia();
};

/** 视频聊天模块 */
const sendVideoCall = () => {
    imSocket.socket.emit("sdp_call", {
        userId: socketStore.userId,
        callId: chatModel.value.otherId,
        callType: CALL_TYPE.VIDEO
    });
    waitCallModel.value.title = `等待${chatModel.value.otherName}应答...`;
    waitCallModel.value.visible = true;
};
const startVideo = async () => {
    await createPeer();
    await openMedia();
    sendCall();
};
const closeVideo = () => {
    messageApi.info("通话已结束");
    sdpModel.value.audioVisible = false;
    sdpModel.value.videoVisible = false;
    callType.value = CALL_TYPE.TEXT;
    imSocket.socket.emit("sdp_close", {
        callId: chatModel.value.otherId
    });
    closeMedia();
};

const onSdpCall = (data: any) => {
    let type = data.callType === CALL_TYPE.AUDIO ? "语音通话" : "视频通话";
    confirmCallModel.value.title = `用户: ${data.callName}向你发送${type}请求`;
    confirmCallModel.value.callId = data.callId;
    confirmCallModel.value.visible = true;
    confirmCallModel.value.callType = data.callType;
};
const onSdpRes = (data: any) => {
    if (data.isAgree) {
        waitCallModel.value.visible = false;
        // 同意，下一步
        callType.value = data.callType;
        if (data.callType === CALL_TYPE.AUDIO) {
            sdpModel.value.audioVisible = true;
            startAudio();
        } else {
            sdpModel.value.videoVisible = true;
            startVideo();
        }
    } else {
        waitCallModel.value.visible = false;
        messageApi.error("对方拒绝了你的聊天");
    }
};
const onSdpClose = () => {
    messageApi.info("通话已结束");
    sdpModel.value.audioVisible = false;
    sdpModel.value.videoVisible = false;
    confirmCallModel.value.visible = false;
    callType.value = CALL_TYPE.TEXT;
    closeMedia();
};

/** 视频部分 */
const videoCallerRef = ref();
const videoRecieverRef = ref();
const videoRecieverAudioRef = ref();

const closeMedia = () => {
    console.log("关闭前", socketStore.stream?.getTracks());

    socketStore.stream?.getTracks().forEach((track) => {
        track.stop();
    });
    socketStore.stream = null;
    if (audioNode.value) {
        audioNode.value.stop();
        audioNode.value = null;
    }
    if (videoCallerRef.value && videoRecieverRef.value) {
        videoCallerRef.value.srcObject = null;
        videoRecieverRef.value.srcObject = null;
    }

    let senders = socketStore.peerConn?.getSenders();
    senders?.forEach((sender) => {
        socketStore.peerConn?.removeTrack(sender);
    });
};
const createPeer = () => {
    peer.createPeerConn(peer.config);
    console.log(socketStore.peerConn);
};

const audioNode = ref();
const audioRef = ref();
// 开启视频
const openMedia = async () => {
    // const videoWidth = document.querySelector(".video1")?.clientWidth;
    // const videoHeight = document.querySelector(".video1")?.clientHeight;

    // 获取视频流，保存
    const stream = await navigator.mediaDevices.getUserMedia({
        audio: true,
        video: callType.value === CALL_TYPE.VIDEO ? true : false
    });
    socketStore.stream = stream;
    if (callType.value === CALL_TYPE.VIDEO) {
        videoCallerRef.value.srcObject = stream;
        videoCallerRef.value.onloadedmetadata = function () {
            videoCallerRef.value.play();
        };
        // 将音视频源放入RTCP
        peer.getRemoteStream((stream: MediaStream, type: string) => {
            console.log("对方视频流", stream, type);
            if (type === "video") {
                videoRecieverRef.value.srcObject = stream;
                console.log("添加视频", videoRecieverRef.value, videoRecieverRef.value.srcObject);
                videoRecieverRef.value.onloadedmetadata = function () {
                    console.log("视频加载完成");
                    videoRecieverRef.value.play();
                };
            } else {
                videoRecieverAudioRef.value.srcObject = stream;
                console.log("添加音频", videoRecieverAudioRef.value);
                videoRecieverAudioRef.value.onloadedmetadata = function () {
                    console.log("语音加载完成");
                    videoRecieverAudioRef.value.play();
                };
            }
        });
    } else {
        peer.getRemoteStream((stream: MediaStream, type: string) => {
            // 获取远程音频轨道
            if (type === "audio") {
                audioRef.value.srcObject = stream;
                audioRef.value.onloadedmetadata = function () {
                    console.log("语音加载完成");
                    audioRef.value.play();
                };
            }
        });
    }
    peer.bindTracks();
};

const sendCall = async () => {
    // 呼叫对方
    peer.call(
        (req: any) => {
            imSocket.socket.emit("sdp", {
                callId: chatModel.value.otherId,
                ...req
            });
        },
        () => {
            console.log("发送给对方的offer", socketStore.localOffer);
            let req = {
                callId: chatModel.value.otherId,
                type: "offer",
                content: socketStore.localOffer
            };
            imSocket.socket.emit("sdp", req);
        }
    );
};

const onSdp = async (data: any) => {
    switch (data.type) {
        case "offer":
        case "candidate": {
            peer.answer_p2(
                data,
                createPeer,
                openMedia,
                () => {
                    let req = {
                        type: "answer",
                        callId: chatModel.value.otherId,
                        content: socketStore.localOffer
                    };
                    imSocket.socket.emit("sdp", req);
                },
                (req: any) => {
                    imSocket.socket.emit("sdp", {
                        callId: chatModel.value.otherId,
                        ...req
                    });
                }
            );
            break;
        }
        case "answer": {
            await peer.answer_p1(data);
            messageApi.success("成功与对方建立连接!");
        }
    }
};
</script>

<style scoped lang="scss">
.ant-input {
    width: 300px;
}
.header {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
}

.chat-wrapper {
    width: 80%;
    height: calc(100vh - 190px);
    border: 1px solid #d4dde1;
    border-radius: 8px;
    .chat-title {
        border-radius: 8px 8px 0 0;
        background-color: #e7f5ff;
        height: 40px;
        padding-left: 24px;
        line-height: 40px;
        font-size: 17px;
        font-weight: 600;
        display: flex;
        align-items: center;
        justify-content: space-between;
        .ant-btn {
            font-weight: 600;
            color: #d23918;
        }
    }
    .user-name {
        display: none;
    }
    .video {
        width: 300px;
        height: 200px;
        border: 2px solid #909399;
        border-radius: 4px;
        margin-top: 10px;
        margin-right: 20px;
    }
    .chat-content {
        width: 100%;
        height: calc(100vh - 210px - 110px);
        padding: 12px;
        margin-top: 12px;
        overflow: auto;
        .chat-message {
            width: 100%;
            border-radius: 0 8px 8px 8px;
            margin-bottom: 20px;
            display: flex;
            flex-wrap: wrap;
            span {
                max-width: 80%;
                display: inline-block;
                padding: 8px 12px;
                border-radius: 8px;
                word-wrap: break-word;
            }
            .left {
                margin-right: auto;
                background-color: #aed0ee;
            }
            .right {
                margin-left: auto;
                background-color: #efefef;
            }
        }
    }
    .message-wrapper {
        width: 100%;
        margin-top: 12px;
        border-top: 1px solid #d4dde1;
        padding: 12px;
        .ant-input {
            width: 50%;
            margin-bottom: 12px;
        }
        .ant-btn {
            margin-left: 12px;
        }
        .btn-send {
            margin-right: 12px;
        }
        .btn-audio {
            margin-left: 0;
        }
    }
}
.video-content {
    width: 100%;
    height: calc(100vh - 170px);
    position: relative;
    border: 1px solid #d4dde1;
    border-radius: 8px;
    display: flex;
    flex-wrap: wrap;
    background-color: #2c2f3b;
    .video1 {
        width: 50%;
        height: 80%;
    }
    .video2 {
        width: 50%;
        height: 80%;
    }
    .ant-btn {
        width: 80px;
        height: 40px;
        margin: auto;
    }
}
.audio-content {
    width: 60%;
    height: calc(100vh - 190px);
    border: 1px solid #d4dde1;
    border-radius: 8px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    background-color: #5c4f55;
    .otherName {
        margin-top: 20px;
        font-weight: 600;
        font-size: 26px;
        color: #fff;
    }
    .ant-avatar {
        margin-bottom: 60px;
    }
    .ant-btn {
        width: 80px;
        height: 40px;
        margin-top: 80px;
    }
}
.members {
    flex: 1;
    border: 1px solid #606266;
    border-radius: 8px;
    padding: 12px;
}

@media screen and (max-width: 800px) {
    .a-input {
        width: 100%;
    }
    .chat-wrapper {
        width: 100%;
        height: calc(100vh - 140px);
        h3 {
            display: flex;
            flex-direction: column;
            .user-name {
                display: inline-block;
                margin-left: 12px;
            }
        }
        .video {
            width: 100%;
        }
    }
    .audio-content {
        width: 100%;
    }
    .video-content {
        flex-direction: column;
        .video1 {
            width: 40%;
            position: absolute;
            top: -100px;
            left: 0;
            z-index: 100;
        }
        .video2 {
            width: 100%;
            height: 80%;
            position: absolute;
        }
        .ant-btn {
            position: absolute;
            width: 80px;
            height: 40px;
            bottom: 50px;
            left: 50%;
            transform: translateX(-50%);
        }
    }
    .isBeCalled {
        .video1 {
            position: absolute;
            width: 30%;
            top: 0;
            left: 0;
            z-index: 2;
        }
        .video2 {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            width: 100%;
            height: 100%;
            z-index: 1;
        }
    }
}
</style>
